package com.nulldev.util.internal.backport.httpclient_rw.backports;

import java.util.Iterator;

/* This is useful for the nightmare of parsing multi-part HTTP/RFC822 headers
 * sensibly:
 * From a String like: 'timeout=15, max=5'
 * create an array of Strings:
 * { {"timeout", "15"},
 *   {"max", "5"}
 * }
 * From one like: 'Basic Realm="FuzzFace" Foo="Biz Bar Baz"'
 * create one like (no quotes in literal):
 * { {"basic", null},
 *   {"realm", "FuzzFace"}
 *   {"foo", "Biz Bar Baz"}
 * }
 * keys are converted to lower case, vals are left as is....
 *
 * @author Dave Brown
 */

public class HeaderParser {

	/* table of key/val pairs */
	String raw;
	String[][] tab;
	int nkeys;
	int asize = 10; // initial size of array is 10

	public HeaderParser(String raw) {
		this.raw = raw;
		tab = new String[asize][2];
		parse();
	}

	private HeaderParser() {
	}

	/**
	 * create a new HeaderParser from this, whose keys (and corresponding values)
	 * range from "start" to "end-1"
	 */
	public HeaderParser subsequence(int start, int end) {
		if (start == 0 && end == nkeys) {
			return this;
		}
		if (start < 0 || start >= end || end > nkeys)
			throw new IllegalArgumentException("invalid start or end");
		HeaderParser n = new HeaderParser();
		n.tab = new String[asize][2];
		n.asize = asize;
		System.arraycopy(tab, start, n.tab, 0, (end - start));
		n.nkeys = (end - start);
		return n;
	}

	private void parse() {

		if (raw != null) {
			raw = raw.trim();
			char[] ca = raw.toCharArray();
			int beg = 0, end = 0, i = 0;
			boolean inKey = true;
			boolean inQuote = false;
			int len = ca.length;
			while (end < len) {
				char c = ca[end];
				if ((c == '=') && !inQuote) { // end of a key
					tab[i][0] = new String(ca, beg, end - beg).toLowerCase();
					inKey = false;
					end++;
					beg = end;
				} else if (c == '\"') {
					if (inQuote) {
						tab[i++][1] = new String(ca, beg, end - beg);
						inQuote = false;
						do {
							end++;
						} while (end < len && (ca[end] == ' ' || ca[end] == ','));
						inKey = true;
						beg = end;
					} else {
						inQuote = true;
						end++;
						beg = end;
					}
				} else if (c == ' ' || c == ',') { // end key/val, of whatever we're in
					if (inQuote) {
						end++;
						continue;
					} else if (inKey) {
						tab[i++][0] = (new String(ca, beg, end - beg)).toLowerCase();
					} else {
						tab[i++][1] = (new String(ca, beg, end - beg));
					}
					while (end < len && (ca[end] == ' ' || ca[end] == ',')) {
						end++;
					}
					inKey = true;
					beg = end;
				} else {
					end++;
				}
				if (i == asize) {
					asize = asize * 2;
					String[][] ntab = new String[asize][2];
					System.arraycopy(tab, 0, ntab, 0, tab.length);
					tab = ntab;
				}
			}
			// get last key/val, if any
			if (--end > beg) {
				if (!inKey) {
					if (ca[end] == '\"') {
						tab[i++][1] = (new String(ca, beg, end - beg));
					} else {
						tab[i++][1] = (new String(ca, beg, end - beg + 1));
					}
				} else {
					tab[i++][0] = (new String(ca, beg, end - beg + 1)).toLowerCase();
				}
			} else if (end == beg) {
				if (!inKey) {
					if (ca[end] == '\"') {
						tab[i++][1] = String.valueOf(ca[end - 1]);
					} else {
						tab[i++][1] = String.valueOf(ca[end]);
					}
				} else {
					tab[i++][0] = String.valueOf(ca[end]).toLowerCase();
				}
			}
			nkeys = i;
		}

	}

	public String findKey(int i) {
		if (i < 0 || i > asize)
			return null;
		return tab[i][0];
	}

	public String findValue(int i) {
		if (i < 0 || i > asize)
			return null;
		return tab[i][1];
	}

	public String findValue(String key) {
		return findValue(key, null);
	}

	public String findValue(String k, String Default) {
		if (k == null)
			return Default;
		k = k.toLowerCase();
		for (int i = 0; i < asize; ++i) {
			if (tab[i][0] == null) {
				return Default;
			} else if (k.equals(tab[i][0])) {
				return tab[i][1];
			}
		}
		return Default;
	}

	class ParserIterator implements Iterator<String> {
		int index;
		boolean returnsValue; // or key

		ParserIterator(boolean returnValue) {
			returnsValue = returnValue;
		}

		public boolean hasNext() {
			return index < nkeys;
		}

		public String next() {
			return tab[index++][returnsValue ? 1 : 0];
		}

		public void remove() {
			throw new UnsupportedOperationException("remove not supported");
		}
	}

	public Iterator<String> keys() {
		return new ParserIterator(false);
	}

	public Iterator<String> values() {
		return new ParserIterator(true);
	}

	public String toString() {
		Iterator<String> k = keys();
		StringBuilder sb = new StringBuilder();
		sb.append("{size=").append(asize).append(" nkeys=").append(nkeys).append(' ');
		for (int i = 0; k.hasNext(); i++) {
			String key = k.next();
			String val = findValue(i);
			if (val != null && "".equals(val)) {
				val = null;
			}
			sb.append(" {").append(key).append(val == null ? "" : "," + val).append('}');
			if (k.hasNext()) {
				sb.append(',');
			}
		}
		sb.append(" }");
		return sb.toString();
	}

	public int findInt(String k, int Default) {
		try {
			return Integer.parseInt(findValue(k, String.valueOf(Default)));
		} catch (Throwable t) {
			return Default;
		}
	}
	/*
	 * public static void main(String[] a) throws Exception {
	 * System.out.print("enter line to parse> "); System.out.flush();
	 * DataInputStream dis = new DataInputStream(System.in); String line =
	 * dis.readLine(); HeaderParser p = new HeaderParser(line); for (int i = 0; i <
	 * asize; ++i) { if (p.findKey(i) == null) break; String v = p.findValue(i);
	 * System.out.println(i + ") " +p.findKey(i) + "="+v); }
	 * System.out.println("Done!"); }
	 */
}